﻿using DrawIO45.Models.DrawIOs;
using DrawIO45.Models.Logic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;

namespace DrawIO45.Draws.Business
{
    /// <summary>
    /// 清理掉不是没有被引用根引用的位置
    /// </summary>
    public class ClearNotRefRoot : IDrawOnRoot
    {
        List<string> RefRoots;

        public ClearNotRefRoot(ClearNotRefRootArgs args)
        {
            RefRoots = args.Roots;
        }

        public void DrawOnRoot(Root root, List<DILocationInfo<LClassInfo>> classInfos)
        {
            var rootClass = classInfos.Where(n => RefRoots?.Contains(n.Object.Name) == true).ToList();
            if (rootClass?.Any() != true)
                throw new NotSupportedException();

            var refRootIds = rootClass.Select(n => n.Object.Id).ToList();

            var lines = new List<LineInfo>();

            var lineCells = root.MxCells.Where(IsLine).ToList();
            if (lineCells?.Any() == true)
            {
                foreach (var line in lineCells)
                {
                    lines.Add(new LineInfo
                    {
                        Source = line.Source,
                        Target = line.Target,
                        Name = line.Value,
                    });
                }
            }

            Dictionary<string, DeepResult> IsRefRoot = new Dictionary<string, DeepResult>();

            var refRootNodeIds = new List<string>();

            int maxDeep = 3;

            var GetRefFunc = new Func<LineInfo, int, string[]>(GetRefFromMe);

            var objs = root.MxCells.Where(n => IsLine(n) != true).ToList();
            foreach (var item in objs)
            {
                var touchIds = new List<string>();

                var deepResult = DeepFind(touchIds, item.Id, GetRefFunc, 0);

                //var deepResult = DeepFind(touchIds, item.Id, GetAll, 0);

                if (IsRefRoot.TryGetValue(item.Id, out var result))
                {
                    if (deepResult.Deep < result.Deep)
                        result.Deep = deepResult.Deep;
                }
                else
                {
                    IsRefRoot[item.Id] = deepResult;
                }

                if (deepResult.Success && maxDeep >= deepResult.Deep)
                    refRootNodeIds.Add(item.Id);
            }

            var matchClearItemFunc = new List<Func<MxCell, bool>>
            {
                new Func<MxCell, bool>(mx => RootIds.Contains(mx.Id)),      // 根节点不能删
                //new Func<MxCell, bool>(mx => itemIds.Contains(mx.Id)),      // 有关系的不能删
                //new Func<MxCell, bool>(mx => itemIds.Contains(mx.Parent)),  // 有关系的子类不能删
                
                new Func<MxCell, bool>(mx => refRootNodeIds.Contains(mx.Id)),
                new Func<MxCell, bool>(mx => refRootNodeIds.Contains(mx.Parent)),

                //IsLine,                                                     // 线对象不能删

                //new Func<MxCell, bool>(mx => IsLine(mx) && (refRootNodeIds.Contains(mx.Source) && refRootNodeIds.Contains(mx.Target))),
                IsReservedLine,
            };

            // 任意一个为true 则不能删除
            root.MxCells.RemoveAll(IsCanDelete);

            //foreach (var item in root.MxCells)
            //{
            //    if (IsReservedLine(item))
            //    {
            //        item.Style += "fillColor=#f8cecc;strokeColor=#b85450;";
            //    }
            //}

            //foreach (var item in root.MxCells)
            //{
            //    if (IsRefRoot.TryGetValue(item.Id, out var dResult) != true)
            //        continue;

            //    if (dResult.Success != true)
            //        continue;

            //    item.Value = $"{item.Value} deep:{dResult.Deep}";
            //}

            bool IsReservedLine(MxCell mx)
            {
                if (IsLine(mx) != true)
                    return false;

                if (refRootNodeIds.Contains(mx.Source) != true)
                    return false;

                if (refRootNodeIds.Contains(mx.Target) != true)
                    return false;

                if (mx.Value == "Impl")
                    return true;

                // 保证流向正确
                var sourceClass = classInfos.FirstOrDefault(n => n.Object.Id == mx.Source);
                if (sourceClass == null)
                    return false;

                if (IsRefRoot.TryGetValue(mx.Source, out var sourceDeep) != true)
                    return false;

                var targetClass = classInfos.FirstOrDefault(n => n.Object.Id == mx.Target);
                if (targetClass == null)
                    return false;

                if (IsRefRoot.TryGetValue(mx.Target, out var targetDeep) != true)
                    return false;

                var lineInfo = new LineInfo
                {
                    Name = mx.Value,
                    Source = mx.Source,
                    Target = mx.Target,
                };

                // 正向查询
                var positiveDirectionRefIds = GetRefFunc(lineInfo, sourceDeep.Deep);
                if (positiveDirectionRefIds.Contains(mx.Target))
                {
                    return targetDeep.Deep <= sourceDeep.Deep;
                }

                // 反向查询
                var reverseDirectionRefIds = GetRefFunc(lineInfo, targetDeep.Deep);
                if (reverseDirectionRefIds.Contains(mx.Source))
                {
                    return sourceDeep.Deep <= targetDeep.Deep;
                }

                return false;
            }

            // 深度查找数据
            DeepResult DeepFind(List<string> touchIds, string currentId, Func<LineInfo, int, string[]> directionFunc, int deep = 0)
            {
                if (refRootIds.Contains(currentId))
                {
                    return new DeepResult
                    {
                        Success = true,
                        Deep = deep,
                    };
                }

                if (touchIds.Contains(currentId))
                    return new DeepResult { Success = false };

                touchIds.Add(currentId);

                if (IsRefRoot.TryGetValue(currentId, out var val) == true)
                {
                    return new DeepResult
                    {
                        Success = val.Success,
                        Deep = deep + val.Deep,
                    };
                }

                var otherIds = lines
                    .Where(n => n.Source == currentId || n.Target == currentId)
                    //.SelectMany(n => new[] { n.Source, n.Target })
                    .SelectMany(n => directionFunc(n, deep))
                    .Where(n => n != currentId)
                    .ToList();

                var isFind = false;
                var minDeep = int.MaxValue;

                foreach (var otherId in otherIds)
                {
                    var result = DeepFind(touchIds, otherId, directionFunc, deep + 1);
                    if (result.Success)
                    {
                        isFind = true;
                        if (result.Deep < minDeep)
                            minDeep = result.Deep;
                    }
                }

                return new DeepResult
                {
                    Success = isFind,
                    Deep = isFind ? minDeep : 0,
                };
            }

            bool IsCanDelete(MxCell cell)
            {
                foreach (var item in matchClearItemFunc)
                {
                    if (item(cell))
                        return false;
                }

                return true;
            }
        }

        string[] RootIds = new[] { "0", "1" };

        bool IsLine(MxCell cell)
        {
            if (string.IsNullOrEmpty(cell.Target))
                return false;

            if (string.IsNullOrEmpty(cell.Source))
                return false;

            return true;
        }

        string[] GetAll(LineInfo line, int deep)
        {
            return new string[] { line.Target, line.Source };
        }

        string[] GetRefFromMe(LineInfo line, int deep)
        {
            //if (deep == 0)
            //    return GetAll(line, deep);

            if (string.Equals("Use", line.Name, StringComparison.OrdinalIgnoreCase))
                return new[] { line.Source };

            if (string.Equals("Impl", line.Name, StringComparison.OrdinalIgnoreCase))
                return new[] { line.Source, line.Target };

            return new string[] { line.Target };
        }
    }

    public class DeepResult
    {
        public bool Success { get; set; }

        public int Deep { get; set; }
    }

    public class LineInfo
    {
        public string Source { get; set; }

        public string Target { get; set; }

        public string Name { get; set; }
    }

    public class ClearNotRefRootArgs
    {
        public List<string> Roots { get; set; }

        /// <summary>
        /// 清理旁支，只保留能链接到当前分支对象的
        /// </summary>
        public bool ClearSideBranch { get; set; }
    }
}
